this change improves the following points of the FPS counter:
-cleaner design: less colors, text arranged above the graph, deviation number right aligned -added transparency: FPS counter does not completely cover the webpage -less frequent number updates: makes the numbers easier to read -indicator line at 60fps comparison images: https://docs.google.com/folder/d/0B8Y78t3tjy1XZk1xdWx6VjN5aFE/edit Please download the patch and provide a screenshot from your system to test the layout. Review URL: https://chromiumcodereview.appspot.com/11272042 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166041 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
@ -57,6 +57,25 @@ void FontAtlas::drawOneLineOfTextInternal(SkCanvas* canvas, const SkPaint& paint
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Size FontAtlas::textSize(const std::string& text)
|
||||
{
|
||||
int maxWidth = 0;
|
||||
std::vector<std::string> lines;
|
||||
base::SplitString(text, '\n', &lines);
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
int lineWidth = 0;
|
||||
for (size_t j = 0; j < lines[i].size(); ++j) {
|
||||
int asciiIndex = (lines[i][j] < 128) ? lines[i][j] : 0;
|
||||
lineWidth += m_asciiToRectTable[asciiIndex].width();
|
||||
}
|
||||
if (lineWidth > maxWidth)
|
||||
maxWidth = lineWidth;
|
||||
}
|
||||
|
||||
return gfx::Size(maxWidth, m_fontHeight * lines.size());
|
||||
}
|
||||
|
||||
void FontAtlas::drawDebugAtlas(SkCanvas* canvas, const gfx::Point& destPosition) const
|
||||
{
|
||||
DCHECK(Proxy::isImplThread());
|
||||
|
@ -31,6 +31,9 @@ public:
|
||||
}
|
||||
~FontAtlas();
|
||||
|
||||
// Current font height.
|
||||
int fontHeight() const { return m_fontHeight; }
|
||||
|
||||
// Draws multiple lines of text where each line of text is separated by '\n'.
|
||||
// - Correct glyphs will be drawn for ASCII codes in the range 32-127; any characters
|
||||
// outside that range will be displayed as a default rectangle glyph.
|
||||
@ -39,6 +42,9 @@ public:
|
||||
// - Should only be called only on the impl thread.
|
||||
void drawText(SkCanvas*, const SkPaint&, const std::string& text, const gfx::Point& destPosition, const gfx::Size& clip) const;
|
||||
|
||||
// Gives a text's width and height on the canvas.
|
||||
gfx::Size textSize(const std::string& text);
|
||||
|
||||
// Draws the entire atlas at the specified position, just for debugging purposes.
|
||||
void drawDebugAtlas(SkCanvas*, const gfx::Point& destPosition) const;
|
||||
|
||||
|
@ -70,9 +70,10 @@ void FrameRateCounter::markEndOfFrame()
|
||||
|
||||
bool FrameRateCounter::isBadFrameInterval(base::TimeDelta intervalBetweenConsecutiveFrames) const
|
||||
{
|
||||
double delta = intervalBetweenConsecutiveFrames.InSecondsF();
|
||||
bool schedulerAllowsDoubleFrames = !Proxy::hasImplThread();
|
||||
bool intervalTooFast = schedulerAllowsDoubleFrames && intervalBetweenConsecutiveFrames.InSecondsF() < kFrameTooFast;
|
||||
bool intervalTooSlow = intervalBetweenConsecutiveFrames.InSecondsF() > kFrameTooSlow;
|
||||
bool intervalTooFast = schedulerAllowsDoubleFrames ? delta < kFrameTooFast : delta <= 0.0;
|
||||
bool intervalTooSlow = delta > kFrameTooSlow;
|
||||
return intervalTooFast || intervalTooSlow;
|
||||
}
|
||||
|
||||
@ -83,11 +84,13 @@ bool FrameRateCounter::isBadFrame(int frameNumber) const
|
||||
|
||||
void FrameRateCounter::getAverageFPSAndStandardDeviation(double& averageFPS, double& standardDeviation) const
|
||||
{
|
||||
int frame = m_currentFrameNumber - 1;
|
||||
averageFPS = 0;
|
||||
int averageFPSCount = 0;
|
||||
int frameNumber = m_currentFrameNumber - 1;
|
||||
int frameCount = 0;
|
||||
double fpsVarianceNumerator = 0;
|
||||
|
||||
averageFPS = 0;
|
||||
standardDeviation = 0;
|
||||
|
||||
// Walk backwards through the samples looking for a run of good frame
|
||||
// timings from which to compute the mean and standard deviation.
|
||||
//
|
||||
@ -98,32 +101,29 @@ void FrameRateCounter::getAverageFPSAndStandardDeviation(double& averageFPS, dou
|
||||
// too much for short lived animations.
|
||||
//
|
||||
// isBadFrame encapsulates the frame too slow/frame too fast logic.
|
||||
while (1) {
|
||||
if (!isBadFrame(frame)) {
|
||||
averageFPSCount++;
|
||||
base::TimeDelta secForLastFrame = m_timeStampHistory[frameIndex(frame)] -
|
||||
m_timeStampHistory[frameIndex(frame - 1)];
|
||||
double x = 1.0 / secForLastFrame.InSecondsF();
|
||||
|
||||
// Go through all available historical data.
|
||||
while (frameIndex(frameNumber) != frameIndex(m_currentFrameNumber) && frameNumber >= 0) {
|
||||
base::TimeDelta delta = frameInterval(frameNumber);
|
||||
|
||||
if (!isBadFrameInterval(delta)) {
|
||||
frameCount++;
|
||||
double x = 1.0 / delta.InSecondsF();
|
||||
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)) {
|
||||
averageFPS += deltaFromAverage / frameCount;
|
||||
fpsVarianceNumerator += deltaFromAverage * (x - averageFPS);
|
||||
} else if (frameCount)
|
||||
// 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;
|
||||
}
|
||||
frameNumber--;
|
||||
}
|
||||
|
||||
standardDeviation = sqrt(fpsVarianceNumerator / averageFPSCount);
|
||||
if (frameCount)
|
||||
standardDeviation = sqrt(fpsVarianceNumerator / frameCount);
|
||||
}
|
||||
|
||||
base::TimeTicks FrameRateCounter::timeStampOfRecentFrame(int n)
|
||||
base::TimeTicks FrameRateCounter::timeStampOfRecentFrame(int n) const
|
||||
{
|
||||
DCHECK(n >= 0);
|
||||
DCHECK(n < kTimeStampHistorySize);
|
||||
|
@ -25,7 +25,8 @@ public:
|
||||
|
||||
// n = 0 returns the oldest frame retained in the history,
|
||||
// while n = timeStampHistorySize() - 1 returns the timestamp most recent frame.
|
||||
base::TimeTicks timeStampOfRecentFrame(int n);
|
||||
// FIXME: Returns most recent timestamp for n = 0 when called between markBeginningOfFrame and markEndOfFrame calls.
|
||||
base::TimeTicks timeStampOfRecentFrame(int n) const;
|
||||
|
||||
// This is a heuristic that can be used to ignore frames in a reasonable way. Returns
|
||||
// true if the given frame interval is too fast or too slow, based on constant thresholds.
|
||||
@ -51,7 +52,7 @@ private:
|
||||
// FIXME: Determine this threshold based on monitor refresh rate, crbug.com/138642.
|
||||
static const double kDroppedFrameTime;
|
||||
|
||||
static const int kTimeStampHistorySize = 120;
|
||||
static const int kTimeStampHistorySize = 170;
|
||||
|
||||
int m_currentFrameNumber;
|
||||
base::TimeTicks m_timeStampHistory[kTimeStampHistorySize];
|
||||
|
@ -21,7 +21,7 @@ HeadsUpDisplayLayer::HeadsUpDisplayLayer()
|
||||
: Layer()
|
||||
{
|
||||
|
||||
setBounds(gfx::Size(512, 128));
|
||||
setBounds(gfx::Size(256, 128));
|
||||
}
|
||||
|
||||
HeadsUpDisplayLayer::~HeadsUpDisplayLayer()
|
||||
@ -39,7 +39,7 @@ void HeadsUpDisplayLayer::update(ResourceUpdateQueue&, const OcclusionTracker*,
|
||||
int height = std::min(maxTextureSize, layerTreeHost()->deviceViewportSize().height());
|
||||
bounds = gfx::Size(width, height);
|
||||
} else {
|
||||
bounds = gfx::Size(512, 128);
|
||||
bounds = gfx::Size(256, 128);
|
||||
}
|
||||
|
||||
setBounds(bounds);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "third_party/skia/include/core/SkPaint.h"
|
||||
#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
|
||||
#include "ui/gfx/point.h"
|
||||
#include "ui/gfx/size.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
@ -43,6 +44,8 @@ static inline SkPaint createPaint()
|
||||
|
||||
HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(int id)
|
||||
: LayerImpl(id)
|
||||
, m_averageFPS(0)
|
||||
, m_stdDeviation(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -143,17 +146,10 @@ void HeadsUpDisplayLayerImpl::drawHudContents(SkCanvas* canvas)
|
||||
canvas->drawRect(SkRect::MakeXYWH(0, 0, bounds().width(), bounds().height()), paint);
|
||||
}
|
||||
|
||||
int fpsCounterHeight = 40;
|
||||
int fpsCounterTop = 2;
|
||||
int platformLayerTreeTop;
|
||||
int platformLayerTreeTop = 0;
|
||||
|
||||
if (settings.showFPSCounter)
|
||||
platformLayerTreeTop = fpsCounterTop + fpsCounterHeight;
|
||||
else
|
||||
platformLayerTreeTop = 0;
|
||||
|
||||
if (settings.showFPSCounter)
|
||||
drawFPSCounter(canvas, layerTreeHostImpl()->fpsCounter(), fpsCounterTop, fpsCounterHeight);
|
||||
platformLayerTreeTop = drawFPSCounter(canvas, layerTreeHostImpl()->fpsCounter());
|
||||
|
||||
if (settings.showPlatformLayerTree && m_fontAtlas.get()) {
|
||||
std::string layerTree = layerTreeHostImpl()->layerTreeAsText();
|
||||
@ -164,75 +160,106 @@ void HeadsUpDisplayLayerImpl::drawHudContents(SkCanvas* canvas)
|
||||
drawDebugRects(canvas, layerTreeHostImpl()->debugRectHistory());
|
||||
}
|
||||
|
||||
void HeadsUpDisplayLayerImpl::drawFPSCounter(SkCanvas* canvas, FrameRateCounter* fpsCounter, int top, int height)
|
||||
int HeadsUpDisplayLayerImpl::drawFPSCounter(SkCanvas* canvas, FrameRateCounter* fpsCounter)
|
||||
{
|
||||
float textWidth = 170; // so text fits on linux.
|
||||
float graphWidth = fpsCounter->timeStampHistorySize();
|
||||
const int left = 2;
|
||||
const int top = 2;
|
||||
|
||||
// Draw the FPS text.
|
||||
drawFPSCounterText(canvas, fpsCounter, top, textWidth, height);
|
||||
const int padding = 4;
|
||||
|
||||
const int fontHeight = m_fontAtlas.get() ? m_fontAtlas->fontHeight() : 0;
|
||||
const int graphWidth = fpsCounter->timeStampHistorySize() - 3;
|
||||
const int graphHeight = 40;
|
||||
|
||||
const int width = graphWidth + 2 * padding;
|
||||
const int height = fontHeight + graphHeight + 4 * padding + 2;
|
||||
|
||||
// Draw FPS graph.
|
||||
const double loFPS = 0;
|
||||
const double hiFPS = 80;
|
||||
SkPaint paint = createPaint();
|
||||
paint.setColor(SkColorSetRGB(154, 205, 50));
|
||||
canvas->drawRect(SkRect::MakeXYWH(2 + textWidth, top, graphWidth, height / 2), paint);
|
||||
|
||||
paint.setColor(SkColorSetRGB(255, 250, 205));
|
||||
canvas->drawRect(SkRect::MakeXYWH(2 + textWidth, top + height / 2, graphWidth, height / 2), paint);
|
||||
|
||||
int graphLeft = static_cast<int>(textWidth + 3);
|
||||
int x = 0;
|
||||
double h = static_cast<double>(height - 2);
|
||||
SkPath path;
|
||||
for (int i = 0; i < fpsCounter->timeStampHistorySize() - 1; ++i) {
|
||||
int j = i + 1;
|
||||
base::TimeDelta delta = fpsCounter->timeStampOfRecentFrame(j) - fpsCounter->timeStampOfRecentFrame(i);
|
||||
|
||||
// Skip plotting this particular instantaneous frame rate if it is not likely to have been valid.
|
||||
if (fpsCounter->isBadFrameInterval(delta)) {
|
||||
x += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
double fps = 1.0 / delta.InSecondsF();
|
||||
|
||||
// Clamp the FPS to the range we want to plot visually.
|
||||
double p = 1 - ((fps - loFPS) / (hiFPS - loFPS));
|
||||
if (p < 0)
|
||||
p = 0;
|
||||
if (p > 1)
|
||||
p = 1;
|
||||
|
||||
// Plot this data point.
|
||||
SkPoint cur = SkPoint::Make(graphLeft + x, 1 + top + p*h);
|
||||
if (path.isEmpty())
|
||||
path.moveTo(cur);
|
||||
else
|
||||
path.lineTo(cur);
|
||||
x += 1;
|
||||
}
|
||||
paint.setColor(SK_ColorRED);
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(1);
|
||||
paint.setAntiAlias(true);
|
||||
canvas->drawPath(path, paint);
|
||||
}
|
||||
|
||||
void HeadsUpDisplayLayerImpl::drawFPSCounterText(SkCanvas* canvas, FrameRateCounter* fpsCounter, int top, int width, int height)
|
||||
{
|
||||
double averageFPS, stdDeviation;
|
||||
fpsCounter->getAverageFPSAndStandardDeviation(averageFPS, stdDeviation);
|
||||
|
||||
// Draw background.
|
||||
SkPaint paint = createPaint();
|
||||
paint.setColor(SK_ColorBLACK);
|
||||
canvas->drawRect(SkRect::MakeXYWH(2, top, width, height), paint);
|
||||
paint.setColor(SkColorSetARGB(215, 17, 17, 17));
|
||||
canvas->drawRect(SkRect::MakeXYWH(left, top, width, height), paint);
|
||||
|
||||
SkRect textBounds = SkRect::MakeXYWH(left + padding, top + padding, graphWidth, fontHeight);
|
||||
SkRect graphBounds = SkRect::MakeXYWH(left + padding, textBounds.bottom() + 2 * padding, graphWidth, graphHeight);
|
||||
|
||||
drawFPSCounterText(canvas, paint, fpsCounter, textBounds);
|
||||
drawFPSCounterGraph(canvas, paint, fpsCounter, graphBounds);
|
||||
|
||||
return top + height;
|
||||
}
|
||||
|
||||
void HeadsUpDisplayLayerImpl::drawFPSCounterText(SkCanvas* canvas, SkPaint& paint, FrameRateCounter* fpsCounter, SkRect bounds)
|
||||
{
|
||||
// Update FPS text - not every frame so text is readable
|
||||
if (base::TimeDelta(fpsCounter->timeStampOfRecentFrame(0) - textUpdateTime).InSecondsF() > 0.25) {
|
||||
fpsCounter->getAverageFPSAndStandardDeviation(m_averageFPS, m_stdDeviation);
|
||||
textUpdateTime = fpsCounter->timeStampOfRecentFrame(0);
|
||||
}
|
||||
|
||||
// Draw FPS text.
|
||||
if (m_fontAtlas.get())
|
||||
m_fontAtlas->drawText(canvas, createPaint(), base::StringPrintf("FPS: %4.1f +/- %3.1f", averageFPS, stdDeviation), gfx::Point(10, height / 3), gfx::Size(width, height));
|
||||
if (m_fontAtlas.get()) {
|
||||
std::string fpsText = base::StringPrintf("FPS:%5.1f", m_averageFPS);
|
||||
std::string deviationText = base::StringPrintf("+/-%4.1f", m_stdDeviation);
|
||||
|
||||
int deviationWidth = m_fontAtlas->textSize(deviationText).width();
|
||||
gfx::Size textArea(bounds.width(), bounds.height());
|
||||
|
||||
paint.setColor(SK_ColorRED);
|
||||
m_fontAtlas->drawText(canvas, paint, fpsText, gfx::Point(bounds.left(), bounds.top()), textArea);
|
||||
m_fontAtlas->drawText(canvas, paint, deviationText, gfx::Point(bounds.right() - deviationWidth, bounds.top()), textArea);
|
||||
}
|
||||
}
|
||||
|
||||
void HeadsUpDisplayLayerImpl::drawFPSCounterGraph(SkCanvas* canvas, SkPaint& paint, FrameRateCounter* fpsCounter, SkRect bounds)
|
||||
{
|
||||
const double loFPS = 0;
|
||||
const double hiFPS = 80;
|
||||
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(1);
|
||||
|
||||
// Draw top and bottom line.
|
||||
paint.setColor(SkColorSetRGB(150, 150, 150));
|
||||
canvas->drawLine(bounds.left(), bounds.top() - 1, bounds.right(), bounds.top() - 1, paint);
|
||||
canvas->drawLine(bounds.left(), bounds.bottom(), bounds.right(), bounds.bottom(), paint);
|
||||
|
||||
// Draw 60fps line.
|
||||
paint.setColor(SkColorSetRGB(100, 100, 100));
|
||||
canvas->drawLine(bounds.left(), bounds.top() + bounds.height() / 4, bounds.right(), bounds.top() + bounds.height() / 4, paint);
|
||||
|
||||
// Draw FPS graph.
|
||||
int x = 0;
|
||||
SkPath path;
|
||||
|
||||
for (int i = 1; i < fpsCounter->timeStampHistorySize() - 1; ++i) {
|
||||
base::TimeDelta delta = fpsCounter->timeStampOfRecentFrame(i + 1) - fpsCounter->timeStampOfRecentFrame(i);
|
||||
|
||||
// Skip plotting this particular instantaneous frame rate if it is not likely to have been valid.
|
||||
if (!fpsCounter->isBadFrameInterval(delta)) {
|
||||
double fps = 1.0 / delta.InSecondsF();
|
||||
|
||||
// Clamp the FPS to the range we want to plot visually.
|
||||
double p = 1 - ((fps - loFPS) / (hiFPS - loFPS));
|
||||
if (p < 0)
|
||||
p = 0;
|
||||
if (p > 1)
|
||||
p = 1;
|
||||
|
||||
// Plot this data point.
|
||||
SkPoint cur = SkPoint::Make(bounds.left() + x, bounds.top() + p * bounds.height());
|
||||
if (path.isEmpty())
|
||||
path.moveTo(cur);
|
||||
else
|
||||
path.lineTo(cur);
|
||||
}
|
||||
|
||||
x += 1;
|
||||
}
|
||||
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(SK_ColorRED);
|
||||
canvas->drawPath(path, paint);
|
||||
}
|
||||
|
||||
void HeadsUpDisplayLayerImpl::drawDebugRects(SkCanvas* canvas, DebugRectHistory* debugRectHistory)
|
||||
|
@ -6,12 +6,15 @@
|
||||
#define CC_HEADS_UP_DISPLAY_LAYER_IMPL_H_
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/time.h"
|
||||
#include "cc/cc_export.h"
|
||||
#include "cc/font_atlas.h"
|
||||
#include "cc/layer_impl.h"
|
||||
#include "cc/scoped_texture.h"
|
||||
|
||||
class SkCanvas;
|
||||
class SkPaint;
|
||||
struct SkRect;
|
||||
|
||||
namespace cc {
|
||||
|
||||
@ -44,13 +47,19 @@ private:
|
||||
virtual const char* layerTypeAsString() const OVERRIDE;
|
||||
|
||||
void drawHudContents(SkCanvas*);
|
||||
void drawFPSCounter(SkCanvas*, FrameRateCounter*, int top, int height);
|
||||
void drawFPSCounterText(SkCanvas*, FrameRateCounter*, int top, int width, int height);
|
||||
int drawFPSCounter(SkCanvas*, FrameRateCounter*);
|
||||
void drawFPSCounterText(SkCanvas*, SkPaint&, FrameRateCounter*, SkRect);
|
||||
void drawFPSCounterGraph(SkCanvas*, SkPaint&, FrameRateCounter*, SkRect);
|
||||
void drawDebugRects(SkCanvas*, DebugRectHistory*);
|
||||
|
||||
scoped_ptr<FontAtlas> m_fontAtlas;
|
||||
scoped_ptr<ScopedTexture> m_hudTexture;
|
||||
scoped_ptr<SkCanvas> m_hudCanvas;
|
||||
|
||||
double m_averageFPS;
|
||||
double m_stdDeviation;
|
||||
|
||||
base::TimeTicks textUpdateTime;
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
|
Reference in New Issue
Block a user